<?php
	/**
	 * Application class.
	 * @author IMP
	 */
	
	abstract class Application implements Emittable {
		abstract protected function onCreate();
		abstract protected function onDestroy();
		abstract protected function onOutput(OutputBuffer &$ob);
		
		public final function __construct(){
			self::register($this);
				
			try {
				OutputBuffer::disableOutput();
				$this->onCreate();
				OutputBuffer::enableOutput();
			}catch(Exception $e){
				OutputBuffer::enableOutput();
				throw $e;
			}
		}
		
		public final function __destruct(){				
			try {
				OutputBuffer::disableOutput();
				$this->onDestroy();
				OutputBuffer::enableOutput();
			}catch(Exception $e){
				OutputBuffer::enableOutput();
				throw $e;
			}
		}
		
		public final function doOutput(){
			$dob = new OutputBuffer();
			$this->onOutput($dob);
			$dob->close();
			
			return $dob;
		}
		
		public final function emit(){
			return $this->doOutput()->getContents();
		}
		
		public final function __toString(){
			return $this->emit();
		}
		
		private static $apps = array();
		
		private static final function register(Application &$t){
			$name = get_class($t);
			
			if(array_key_exists($name, self::$apps)){
				throw new ApplicationException('Only one instance of an application may run at a time.');
			}else{
				self::$apps[$name] = $t;
			}
		}
		
		const CLASS_MISSING = -1,
			  INVALID_CLASS = -2,
			  OK = true;
		
		private static final function approve(File $file, $class){
			$pfp = new PHPFileParser($file);
			
			if($pfp->definesClass($class)){
				$c = $pfp->getClass($class);
				
				if($c->getParent() == get_called_class() && count($c->getModifiers()) == 0){
					return self::OK;
				}else{
					return self::INVALID_CLASS;
				}
			}else{
				return self::CLASS_MISSING;
			}
		}
		
		public static final function load(File $file, $class, $ignoreExistence = true){
			if(class_exists($class, false) && !$ignoreExistence) throw new ApplicationException('Class ' . $class . ' already exists.');
			
			$status = self::approve($file, $class);
			$pfp = new PHPFileParser($file);
			
			if($status == self::OK){
				try {
					cc_include_once($file);
					return new $class();
				}catch(ErrorException $e){
					throw new ApplicationException('Error while including ' . $file . '.', $e);
				}
			}elseif($status == self::INVALID_CLASS){
				throw new ApplicationException('Class ' . $class . ' (' . $c->getParent() . ') in ' . $file . ' does not extend the necessary class ' . get_called_class() . ' or has been declared with invalid modifiers.');
			}elseif($status == self::CLASS_MISSING){
				throw new ApplicationException('Class ' . $class . ' in ' . $file . ' does not exist.');
			}
		}
		
		private static $appManifests = array();
		
		public static final function registerShortcut($name, File $file, $class){
			if(array_key_exists($name, self::$appManifests)) throw new ApplicationException('Shortcut ' . $name . ' already exists.');
			
			$status = self::approve($file, $class);
			$pfp = new PHPFileParser($file);
			
			if($status == self::OK){
				self::$appManifests[$name] = array('file' => $file, 'class' => $class);
			}elseif($status == self::INVALID_CLASS){
				throw new ApplicationException('Class ' . $class . ' (' . $c->getParent() . ') in ' . $file . ' does not extend the necessary class ' . get_called_class() . ' or has been declared with invalid modifiers.');
			}elseif($status == self::CLASS_MISSING){
				throw new ApplicationException('Class ' . $class . ' in ' . $file . ' does not exist.');
			}
		}
	}
?>